package com.margo.project.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NIO实现的服务端读写
 * 参考都有问题，大坑
 * 
 * 参考 https://blog.csdn.net/colwer/article/details/51891356
 * 
 * https://www.cnblogs.com/restart30/p/8252109.html
 * 
 * @author fangxingbang
 *
 */
public class SocketNIOService {
	// 通道管理器
	private Selector selector;
	/**
	 * 获得一个ServerSocket通道，并对该通道做一些初始化的工作
	 * 
	 * @param port
	 *            绑定的端口号
	 * @throws IOException
	 */
	public void initServer(int port) throws IOException {
		// 获得一个ServerSocket通道
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
		// 设置通道为非阻塞
		serverChannel.configureBlocking(false);
		// 将该通道对应的ServerSocket绑定到port端口
		serverChannel.socket().bind(new InetSocketAddress(port));
		// 获得一个通道管理器
		this.selector = Selector.open();
		// 将通道管理器和该通道绑定，并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后，
		// 当该事件到达时，selector.select()会返回，如果该事件没到达selector.select()会一直阻塞。
		serverChannel.register(selector, SelectionKey.OP_ACCEPT);
	}

	/**
	 * 采用轮询的方式监听selector上是否有需要处理的事件，如果有，则进行处理
	 * 
	 * @throws IOException
	 */
	public void listen() throws IOException {
		System.out.println("服务端启动成功！");
		// 轮询访问selector
		while (true) {
			// 当注册的事件到达时，方法返回；否则,该方法会一直阻塞
			selector.select();
			// 获得selector中选中的项的迭代器，选中的项为注册的事件
			Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
			System.out.println(this.selector.selectedKeys().size());
			while (ite.hasNext()) {
				SelectionKey key = (SelectionKey) ite.next();
				// 删除已选的key,以防重复处理
				ite.remove();
				// 客户端请求连接事件
				if (key.isAcceptable()) {
					ServerSocketChannel server = (ServerSocketChannel) key.channel();
					// 获得和客户端连接的通道
					SocketChannel channel = server.accept();
					// 设置成非阻塞
					channel.configureBlocking(false);
					// 在这里可以给客户端发送信息哦
					channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
					// 在和客户端连接成功之后，为了可以接收到客户端的信息，需要给通道设置读的权限。
					channel.register(this.selector, SelectionKey.OP_READ);
					// 获得了可读的事件
				} else if (key.isReadable()) {
					SocketChannel channel = (SocketChannel) key.channel();
					// 创建读取的缓冲区
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					channel.read(buffer);
					byte[] data = buffer.array();
					String msg = new String(data).trim();
					System.out.println("服务端收到信息：" + msg);
					channel.register(this.selector, SelectionKey.OP_WRITE);
				} else if(key.isWritable()) {
					// 服务器可读取消息:得到事件发生的Socket通道
					SocketChannel channel = (SocketChannel) key.channel();
					String msg="我爱北京天安门";
					//将消息编码为字节数组
					byte[] bytes=msg.getBytes("utf-8");
					//根据数组容量创建ByteBuffer  
					ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
					//将字节数组复制到缓冲区  
					writeBuffer.put(bytes);
					//flip操作  
					writeBuffer.flip();
					//发送缓冲区的字节数组  
					channel.write(writeBuffer);// 将消息回送给客户端
					channel.close();
					//key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
					
				} 
			}

		}
	}

	public static void main(String[] args) throws IOException {
		SocketNIOService server = new SocketNIOService(); 
		server.initServer(8080); 
		server.listen(); 
	}

}
